#include <cstring>
#include <iostream>
#include <functional>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <thread>
#include <vector>
#include <chrono>
#include "message.h"
#include "network.h"
#include "learner.h"
#include "proposer.h"
#include "pthread.h"
#include "acceptor.h"
#include "signal.h"

void testBasicNetwork() {
    std::cout << "TestBasicNetowk........................"
              << "\n";
    std::unordered_map<int, struct sockaddr_in> nodes = {
        {
            1,
            {AF_INET, htons(9600), inet_addr("0.0.0.0")},

        },
        {
            3,
            {AF_INET, htons(9600 + 3), inet_addr("0.0.0.0")},
        },
        {
            5,
            {AF_INET, htons(9600 + 5), inet_addr("0.0.0.0")},
        },
        {
            2,
            {AF_INET, htons(9600 + 2), inet_addr("0.0.0.0")},
        },
        {
            4,
            {AF_INET, htons(9600 + 4), inet_addr("0.0.0.0")},
        },
    };
    std::unordered_map<int, std::shared_ptr<Network>> nets;
    for (auto &node : nodes) {
        nets[node.first] = std::make_shared<Network>(node.first, nodes);
        nets[node.first]->openTcpServer();
    }
    auto recvFunc = [&]() {
        for (auto &net : nets) {
            std::vector<Message> msgVec;
            int ret = net.second->recvTcpMsg(msgVec);
            if (ret <= 0) {
                std::cout << "recv message error " << strerror(errno) << "\n";
            }
        }
    };
    auto m1 = Message{Message::msgType::Prepare, 3, 1, 1, 0, "m1"};
    nets[m1.from]->sendTcpMsg(m1);
    auto m2 = Message{Message::msgType::Accept, 5, 3, 2, 1, "m2"};
    nets[m2.from]->sendTcpMsg(m2);
    auto m3 = Message{Message::msgType::Prepare, 4, 2, 3, 2, "m3"};
    nets[m3.from]->sendTcpMsg(m3);
    std::thread recvThread(recvFunc);
    if (recvThread.joinable()) {
        recvThread.join();
    }
    for (auto &net : nets) {
        net.second->closeTcpServer();
    }
}

void testTwoProsers() {
    std::cout << "TestProserFunction........................"
              << "\n";
    std::unordered_map<int, struct sockaddr_in> nodes = {
        {
            100,
            {AF_INET, htons(9600 + 100), inet_addr("0.0.0.0")},

        },
        {
            1,
            {AF_INET, htons(9600 + 1), inet_addr("0.0.0.0")},

        },
        {
            2,
            {AF_INET, htons(9600 + 2), inet_addr("0.0.0.0")},
        },
        {
            3,
            {AF_INET, htons(9600 + 3), inet_addr("0.0.0.0")},
        },
        {
            200,
            {AF_INET, htons(9600 + 200), inet_addr("0.0.0.0")},
        },
        {
            101,
            {AF_INET, htons(9600 + 101), inet_addr("0.0.0.0")},
        },
    };
    std::unordered_map<int, std::shared_ptr<Network>> nets;
    std::vector<std::thread> tcpServerThreadVec;
    for (auto &node : nodes) {
        nets[node.first] = std::make_shared<Network>(node.first, nodes);
        int ret = nets[node.first]->openTcpServer();
        if (ret < 0) {
            exit(-1);
        }
        tcpServerThreadVec.emplace_back(
            std::thread([&]() { nets[node.first]->recvLoop(); }));
        tcpServerThreadVec.back().detach();
    }
    for (auto &node : nodes) {
        int ret = nets[node.first]->openTcpClient();
        if (ret < 0) {
            exit(-1);
        }
    }
    std::vector<Acceptor> acceptors;
    for (int i = 1; i <= 3; i++) {
        acceptors.emplace_back(Acceptor{i, nets[i], {200}});
    }
    Proposer proposer1{100, "ExpectValue", nets[100], {1, 2, 3}};
    Proposer proposer2{101, "WrongValue", nets[101], {1, 2, 3}};
    Learner learner1{200, nets[200], {1, 2, 3}};
    auto proposerRun = [](Proposer &&proposer) { proposer.run(); };
    std::thread proposer1Thread(proposerRun, proposer1);
    proposer1Thread.detach();
    auto acceptorRun = [](Acceptor &&acceptor) { acceptor.run(); };
    std::vector<std::thread> acceptorThreads;
    for (auto &acceptor : acceptors) {
        acceptorThreads.emplace_back(std::thread(acceptorRun, acceptor));
        acceptorThreads.back().detach();
    }
    std::thread proposer2Thread(proposerRun, proposer2);
    proposer2Thread.detach();
    std::string learnValue = learner1.run();
    std::cout << "finished " << learnValue << "\n";
    std::cout << "proposer1 " << proposer1Thread.get_id() << std::endl;
    std::cout << "proposer2 " << proposer2Thread.get_id() << std::endl;
    for (auto &acceptor : acceptorThreads) {
        std::cout << "acceptor " << acceptor.get_id() << std::endl;
    }
    for (auto &tcpServer : tcpServerThreadVec) {
        std::cout << "tcpServer " << tcpServer.get_id() << std::endl;
    }
    for (auto &net : nets) {
        net.second->closeTcpServer();
        net.second->closeTcpClient();
    }
}
int main() {
    // testBasicNetwork();
    testTwoProsers();
    return 0;
}